package service

import (
	sqlc "bgs/db/sqlc"
	"bgs/grpc/gen/proxy2/wxpay"
	"bgs/grpc/imp/constants"
	"bgs/service/model"
	"bgs/util"
	"context"
	"strconv"

	"database/sql"
	"fmt"
)

// UpdateWithdrawApplymentParam 更新提现申请记录参数
type UpdateWithdrawApplymentParam struct {
	BrandAccountWithdrawApplymentPtr *sqlc.BrandAccountWithdrawApplyment
	ApplyState                       string
	WxWithdrawID                     string
}

// UpdateWithdrawApplyment 更新提现申请记录
func (s *BrandServiceImp) UpdateWithdrawApplyment(ctx context.Context, q *sqlc.Queries, p UpdateWithdrawApplymentParam) (brandAccountWithdrawApplyment sqlc.BrandAccountWithdrawApplyment, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	brandAccountWithdrawApplyment, err = q.UpdateBrandAccountWithdrawApplymentApplyStateAndWxWithdrawID(ctx, sqlc.UpdateBrandAccountWithdrawApplymentApplyStateAndWxWithdrawIDParams{
		ID:           p.BrandAccountWithdrawApplymentPtr.ID,
		ApplyState:   p.ApplyState,
		WxWithdrawID: sqlc.NSFromString(p.WxWithdrawID),
	})
	if err != nil {
		return
	}
	slog.Infof("1 更新提现申请记录 ok")
	return
}

// SubmitAccountWithdrawApplymentParam 提交商户提现申请参数
type SubmitAccountWithdrawApplymentParam struct {
	BrandID  int64
	UserID   int64
	Amount   int64
	Remark   string
	BankMemo string
}

// SubmitAccountWithdrawApplyment 提交商户提现申请
func (s *BrandServiceImp) SubmitAccountWithdrawApplyment(ctx context.Context, q *sqlc.Queries, p SubmitAccountWithdrawApplymentParam) (brandAccountWithdrawApplyment sqlc.BrandAccountWithdrawApplyment, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	brandID := p.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}

	brandBankCard, sqlErr := q.GetLatestBrandBankCardByBrandID(ctx, brandID)
	if sqlErr != nil {
		if sql.ErrNoRows == sqlErr {
			err = fmt.Errorf("没有设置提现银行卡")
		} else {
			err = sqlErr
		}
		return
	}
	slog.Infof("1 获取品牌在用的提现银行卡 ok")

	brandAccountWithdrawApplyment, err = q.CreateBrandAccountWithdrawApplyment(ctx, sqlc.CreateBrandAccountWithdrawApplymentParams{
		BrandID:         brandID,
		BrandBankCardID: brandBankCard.ID,
		UserID:          p.UserID,
		SubMchID:        cBrand.Ext.WxpayMchIDAsSub,
		MchID:           cBrand.Ext.WxpayMchIDAsService,
		Amount:          p.Amount,
		Remark:          sqlc.NSFromString(p.Remark),
		BankMemo:        sqlc.NSFromString(p.BankMemo),
	})
	if err != nil {
		return
	}
	slog.Infof("2 创建商家提现申请 ok")

	outRequestNo, err := s.subMchWithdraw(ctx, &brandAccountWithdrawApplyment)
	if err != nil {
		return
	}
	slog.Infof("3 二级商户账户提现申请 ok")

	brandAccountWithdrawApplyment, err = q.UpdateBrandAccountWithdrawApplymentOutRequestNo(ctx, sqlc.UpdateBrandAccountWithdrawApplymentOutRequestNoParams{
		ID:           brandAccountWithdrawApplyment.ID,
		OutRequestNo: sqlc.NSFromString(outRequestNo),
	})

	if err != nil {
		return
	}
	slog.Infof("4 更新商家提现申请的外部请求单号 ok")
	return
}

// ApplymentQuery wxpay 商户进件查询
func (s *BrandServiceImp) ApplymentQuery(ctx context.Context, wxpayApplymentPtr *sqlc.WxpayApplyment) (applyPtr *wxpay.ApplymentQueryResponse, err error) {

	slog := util.NewContextLogger(ctx, s.slog)

	brandID := wxpayApplymentPtr.BrandID
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}
	sgID := cBrand.SgID

	req := &wxpay.ApplymentQueryRequest{
		BrandId: strconv.FormatInt(sgID, 10),
		ApplyNo: wxpayApplymentPtr.ApplyNo.String,
		Force:   true,
	}
	slog.Infof("ApplymentQuery req:%v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	applyPtr, err = s.proxy.WxpayCl.ApplymentQuery(ctxWithTimout, req)
	if err != nil {
		return
	}
	slog.Infof("wxpay查询商户进件状态 ok")
	return
}

// modifySettlementAccount wxpay 二级商户账户提现申请
func (s *BrandServiceImp) subMchWithdraw(ctx context.Context, brandAccountWithdrawApplymentPtr *sqlc.BrandAccountWithdrawApplyment) (outRequestNo string, err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	cSyncMappingBrand := s.cacher.FetchSyncMappingByLocalObjAndLocalPk(sqlc.GetSyncMappingByLocalObjAndLocalPkParams{
		LocalObj: constants.SyncLocalObjBrand,
		LocalPk:  strconv.FormatInt(brandAccountWithdrawApplymentPtr.BrandID, 10),
	})
	if cSyncMappingBrand == nil {
		err = fmt.Errorf("not found sg_id of brandID:%d", brandAccountWithdrawApplymentPtr.BrandID)
		return
	}
	sgID := cSyncMappingBrand.RemotePk

	bizNo := strconv.FormatInt(brandAccountWithdrawApplymentPtr.ID, 10)

	req := &wxpay.SubMchWithdrawRequest{
		SubMchId: brandAccountWithdrawApplymentPtr.SubMchID,
		MchId:    brandAccountWithdrawApplymentPtr.MchID,
		Amount:   brandAccountWithdrawApplymentPtr.Amount,
		BizNo:    bizNo,
		Remark:   brandAccountWithdrawApplymentPtr.Remark.String,
		BankMemo: brandAccountWithdrawApplymentPtr.BankMemo.String,
		BrandId:  sgID,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	reply, err := s.proxy.WxpayCl.SubMchWithdraw(ctxWithTimout, req)
	if err != nil {
		return
	}
	outRequestNo = reply.OutRequestNo
	return
}

// SetBrandBankCardForSettlementParam 设置商家银行卡参数
type SetBrandBankCardForSettlementParam struct {
	UpsertPtr *sqlc.UpsertBrandBankCardParams
}

// SetBankCardForSettlement 设置商家银行卡(提现/结算)
func (s *BrandServiceImp) SetBankCardForSettlement(ctx context.Context, q *sqlc.Queries, p SetBrandBankCardForSettlementParam) (brandBankCard sqlc.BrandBankCard, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	brandBankCard, err = q.UpsertBrandBankCard(ctx, *p.UpsertPtr)
	if err != nil {
		return
	}
	slog.Infof("1 upsert 商家银行卡 ok")

	err = s.modifySettlementAccount(ctx, &brandBankCard)
	if err != nil {
		return
	}
	slog.Infof("2 修改结算账号 ok")

	return
}

// modifySettlementAccount wxpay 修改结算账号
func (s *BrandServiceImp) modifySettlementAccount(ctx context.Context, brandBankCardPtr *sqlc.BrandBankCard) (err error) {
	// slog := util.NewContextLogger(ctx, s.slog)
	cBrand := s.cacher.FetchBrand(brandBankCardPtr.BrandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandBankCardPtr.BrandID)
		return
	}

	req := &wxpay.SettlementAccountModifyRequest{
		SubMchId:        cBrand.Ext.WxpayMchIDAsSub,
		AccountType:     brandBankCardPtr.AccountType,
		AccountBank:     brandBankCardPtr.AccountBank,
		BankAddressCode: brandBankCardPtr.BankAddressCode,
		BankName:        brandBankCardPtr.BankName,
		AccountNumber:   brandBankCardPtr.AccountNumber,
		MchId:           cBrand.Ext.WxpayMchIDAsService,
	}

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	_, err = s.proxy.WxpayCl.SettlementAccountModify(ctxWithTimout, req)
	if err != nil {
		return
	}
	return
}

// GetSettlementAccountInfo 获取品牌的结算账户信息
func (s *BrandServiceImp) GetSettlementAccountInfo(ctx context.Context, q *sqlc.Queries, brandID int64) (brandSettlementAccountInfoPtr *model.BrandSettlementAccountInfo, err error) {
	slog := util.NewContextLogger(ctx, s.slog)

	brandSettlementAccountInfoPtr = &model.BrandSettlementAccountInfo{}

	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}

	var availableAmount, pendingAmount, withdrawedAmount, withdrawingAmount int64

	if cBrand.Ext.IsWxpayAvailable {
		availableAmount, pendingAmount, err = s.subMchAccountBalanceQuery(ctx, brandID)
		if err != nil {
			return
		}
		slog.Infof("1 二级商户账户实时余额查询请求 ok")

		var noBrandBankCard bool
		brandBankCard, sqlErr := q.GetLatestBrandBankCardByBrandID(ctx, brandID)
		if sqlErr != nil {
			if sql.ErrNoRows != sqlErr {
				return
			}
			noBrandBankCard = true
		}
		slog.Infof("2 获取品牌在用的提现银行卡 ok")
		brandSettlementAccountInfoPtr.AccountBank = brandBankCard.AccountBank
		brandSettlementAccountInfoPtr.AccountNumberSuffix = util.RightString(brandBankCard.AccountNumber, 4)

		if !noBrandBankCard {
			withdrawedAmount, err = q.AggrWithdrawedAmountOfBrandByBrandID(ctx, brandID)
			if err != nil {
				return
			}
			slog.Infof("3 统计已经提现的金额 ok")

			withdrawingAmount, err = q.AggrWithdrawingAmountOfBrandByBrandID(ctx, brandID)
			if err != nil {
				return
			}
			slog.Infof("4 统计提现中的金额 ok")
		} else {
			slog.Infof("3 没有设置银行结算账户(银行卡),无须计算提现金额 ok")
		}
	}

	brandSettlementAccountInfoPtr.AvailableAmount = availableAmount
	brandSettlementAccountInfoPtr.PendingAmount = pendingAmount
	brandSettlementAccountInfoPtr.WithdrawingAmount = withdrawingAmount
	brandSettlementAccountInfoPtr.WithdrawedAmount = withdrawedAmount

	return
}

// subMchAccountBalanceQuery 二级商户账户实时余额查询请求
func (s *BrandServiceImp) subMchAccountBalanceQuery(ctx context.Context, brandID int64) (availableAmount int64, pendingAmount int64, err error) {
	cBrand := s.cacher.FetchBrand(brandID)
	if cBrand == nil {
		err = fmt.Errorf("not found brand of brandID:%d", brandID)
		return
	}

	req := &wxpay.SubMchAccountBalanceQueryRequest{
		SubMchId:    cBrand.Ext.WxpayMchIDAsSub,
		AccountType: wxpay.MchAccountType_BASIC,
		MchId:       cBrand.Ext.WxpayMchIDAsService,
	}

	s.slog.Infof("req WxpayCl SubMchAccountBalanceQuery===>%#v", req)

	ctxWithTimout, cancel := s.proxy.NewContextWith(ctx)
	defer cancel()
	reply, err := s.proxy.WxpayCl.SubMchAccountBalanceQuery(ctxWithTimout, req)
	if err != nil {
		return
	}

	availableAmount = reply.AvailableAmount
	pendingAmount = reply.PendingAmount

	return
}
